package NICE_CORE
import chisel3._
import chisel3.util._

trait Mvm_config{
  val ROW_NUM = 576
  val COL_NUM = 128
  val ROW_BW  = log2Ceil(ROW_NUM)+1
  val COL_BW  = log2Ceil(COL_NUM)+1
}

class cim_rom extends BlackBox with Mvm_config {
  val io = IO(new Bundle{
    val a   = Input(UInt(ROW_BW.W))
    val spo = Output(UInt((COL_NUM*4).W))
  })
}

class cim_rom2 extends Module with Mvm_config {
  val io = IO(new Bundle{
    val a   = Input(UInt(ROW_BW.W))
    val spo = Output(UInt((COL_NUM*4).W))
  })
  val mem = VecInit("h12".U,"h81".U,"hd1".U,"hff".U)
  io.spo := mem(io.a)
}

class mvm_IO extends Bundle with Mvm_config {
  val row_index = UInt(ROW_BW.W)
  val col_index = UInt(COL_BW.W)
  val row_length= UInt(ROW_BW.W)
  val col_length= UInt(COL_BW.W)
}
class cim_mvm extends Module with Mvm_config {
  val io = IO(new Bundle{
    val start     = Input(Bool())
    val mvm_done  = Output(Bool())
    val rcil      = Input(new mvm_IO)
    val push_buf  = Input(UInt((ROW_NUM*2).W))
    val save_buf  = Output(UInt((COL_NUM*16).W))
  })
  val idle :: clr::mvm :: Nil = Enum(3)
  val state = RegInit(idle)
  switch(state){
    is(idle){   state := Mux(io.start,clr,idle)    }
    is(clr) {   state := mvm   }
    is(mvm) {   state := Mux(io.mvm_done,idle,mvm)}
  }

  val input_buf = io.push_buf.asTypeOf(Vec(ROW_NUM,UInt(2.W)))
  val output_buf = RegInit(VecInit(Seq.fill(COL_NUM)(0.S(16.W))))
  val addr = RegInit(UInt(ROW_BW.W),0.U)
  when(state === clr){
    addr := io.rcil.row_index
  }.elsewhen(state===mvm){
    addr := addr + 1.U
  }.otherwise{
    addr := 0.U
  }
  //rom
  val rom = Module(new cim_rom())
  rom.io.a := addr
  val rom_out = (rom.io.spo).asTypeOf(Vec(COL_NUM,SInt(4.W)))
  val output_en = Wire(Vec(COL_NUM,Bool()))
  val add_num   = Wire(Vec(COL_NUM,SInt(16.W)))
  for (i <-0 until COL_NUM ){
    output_en(i) := ( i.U >=io.rcil.col_index)& (i.U < (io.rcil.col_index+io.rcil.col_length))
    add_num(i) := rom_out(i)
    when(state === clr){
      output_buf(i) := 0.S
    }.elsewhen(state===mvm){
       output_buf(i) := Mux(output_en(i),output_buf(i)+
           Mux(input_buf(addr)(1).asBool(),-add_num(i),
           Mux(input_buf(addr)(0).asBool(),add_num(i),0.S)),0.S)
   //   output_buf(i) := output_buf(i)+ rom_out(i)
    }.elsewhen(state===idle){
      output_buf(i) := output_buf(i)
    }
  }
  io.save_buf := output_buf.asUInt()
  io.mvm_done := (addr === (io.rcil.row_index+ io.rcil.row_length-1.U))
}
